import PageLayout from "../components/layout"
import TableOfContents from "../components/table-of-contents"
import Seo from "../components/seo"

export const Head = () => <Seo />
export default PageLayout

# Specimens for Design Systems

Leverage the wide variety of powerful React components of [@lekoarts/gatsby-theme-specimens](https://github.com/LekoArts/gatsby-themes/tree/main/themes/gatsby-theme-specimens) to build your design system. Display your colors, typography or any other design tokens with ease and focus on the design system itself, not on how to showcase it. Works seamlessly with MDX.

[**Source code for this site**](https://github.com/LekoArts/gatsby-starter-specimens).

If you simply want to see how the components look, head over to the [**Preview**](/preview)!

[![GitHub stars](https://img.shields.io/github/stars/LekoArts/gatsby-themes?style=social)](https://github.com/LekoArts/gatsby-themes) [![Twitter Follow](https://img.shields.io/twitter/follow/lekoarts_de?label=Follow&style=social)](https://twitter.com/lekoarts_de) [![npm](https://img.shields.io/npm/v/@lekoarts/gatsby-theme-specimens)](https://www.npmjs.com/package/@lekoarts/gatsby-theme-specimens) [![License](https://img.shields.io/github/license/LekoArts/gatsby-themes)](https://github.com/LekoArts/gatsby-themes/blob/main/LICENSE)

Also be sure to check out other [Free & Open Source Gatsby Themes](https://themes.lekoarts.de) and my [Personal Website](https://www.lekoarts.de?utm_source=specimens&utm_medium=Starter).

## Features

- Theme UI-based theming
- Suitable for MDX
- Offers React components specifically designed for design systems. You can display:
  - Colors as swatches and rows. Individually placed or automated from an object/array in your theme file
  - Typography e.g. font-family, font-size, font-weight and headings
  - Spacing scales
  - Audio files and downloads
  - border-radius or box-shadow
  - Alerts to emphasize important messages

## Installation

```shell
npm install @lekoarts/gatsby-theme-specimens
```

And in your gatsby-config.mjs:

```js
// gatsby-config.mjs
const config = {
  plugins: [
    {
      resolve: `@lekoarts/gatsby-theme-specimens`,
      options: {}
      }
    }
  ]
}

export default config
```

View the [**theme's README**](https://github.com/LekoArts/gatsby-themes/tree/main/themes/gatsby-theme-specimens) to see more instructions on how to set up this theme!

## Specimens

<TableOfContents />

**Please note:** Components wrapped in a `render()` don't need to be written this way in your MDX files - it's only necessary for [`react-live`](https://github.com/FormidableLabs/react-live#liveprovider-) demos on this page.

### Alert

Variants: `alerts.success`, `alerts.hint`, `alerts.warning`, `alerts.info`, `alerts.danger`

Available props:

- type (`string`) (optional) (Default: "hint")

```jsx live
<Alert>Neutral hint - Default</Alert>
```

```jsx live noInline
render(
  <React.Fragment>
    <Alert type="success">Make it so!</Alert>
    <Alert type="hint">Neutral Hint</Alert>
    <Alert type="warning">Gentle warning :)</Alert>
    <Alert type="info">Super helpful information goes here</Alert>
    <Alert type="danger">nooooooooo, not this way</Alert>
  </React.Fragment>
)
```

### Audio

Variant: `audio.specimens`

Available props:

- src (`string`) - Must be a valid path on your webserver, e.g. the sound file below is placed inside src/static/sounds
- name (`string`) (optional) (Default: "")
- desc (`string`) (optional) (Default: "")

```jsx live
<Audio
  src="sounds/through_the_gate.mp3"
  name="Through the Gate"
  desc="Hundreds of years in the future, in a colonized Solar System, police detective Josephus Miller, born on Ceres in the asteroid belt, is sent to find a missing young woman, Juliette 'Julie' Andromeda Mao. James Holden, Executive Officer of the ice hauler Canterbury, is involved in a tragic incident that threatens to destabilize the uneasy peace between Earth, Mars and the Belt."
/>
```

```jsx live
<Audio src="sounds/through_the_gate.mp3" />
```

### Border Radius

Variant: `tables.borderRadius`

Available props:

- radii

radii has to be in the following format:

```
radii: {
  [key: string]: string
}
```

---

```jsx live noInline
const radii = {
  none: "0",
  sm: "0.5rem",
  md: "1rem",
}

render(<BorderRadius radii={radii} />)
```

### Color Families

Variant: `families.specimens`

Available props:

- colors (`Record<string, string[]>`)

colors has to be in the following format:

```
colors: {
  [key: string]: string[]
}
```

---

```jsx live noInline
const colors = {
  blue: ["#60a5fa", "#2563eb"],
  teal: ["#2dd4bf", "#0d9488"],
  orange: {
    50: `#fff7ed`,
    100: `#ffedd5`,
    200: `#fed7aa`,
    300: `#fdba74`,
    400: `#fb923c`,
  },
}

render(<ColorFamilies colors={colors} />)
```

### Color Row

Variant: `rows.specimens`

Available props:

- color (`string`) - Must be in HEX format
- name (`string`)
- prefix (`string`) (optional) (Default: "")

```jsx live
<ColorRow color="#000" name="Blackness" prefix="Dark - " />
```

### Color Swatch

Variant: `swatches.specimens`

Available props:

- color (`string`) - Must be in HEX format
- name (`string`) (optional) (Default: "")
- minimal (`boolean`) (optional) (Default: false)
- prefix (`string`) (optional) (Default: "")

```jsx live noInline
render(
  <React.Fragment>
    <ColorSwatch color="#fff" />
    <ColorSwatch color="#2d3748" name="Blueish" />
    <ColorSwatch color="#667eea" name="Swift" minimal />
    <ColorSwatch color="#4fd1c5" name="4" prefix="Teal - " />
  </React.Fragment>
)
```

### Download

Variant: `download.specimens`

Available props:

- src (`string`) - Must be a valid path on your webserver, e.g. the file below is placed inside src/static/downloads
- name (`string`)

```jsx live
<Download
  name="Gatsby Themes Wallpaper"
  notes="Use for Social Media previews"
  src="downloads/gatsby-themes-wallpaper.jpg"
/>
```

```jsx live
<Download name="Logo" bg="black" src="downloads/logo.png" />
```

### Font Family

Variant: `typography.fontFamily`

Available props:

- fonts
- previewText (`string`) (optional) - Replace "The quick brown fox jumps over the lazy dog" with something custom

fonts has to be in the following format:

```
fonts: {
  [key: string]: string
}
```

---

```jsx live noInline
const fonts = {
  sans: '-apple-system, BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"',
  serif: 'Georgia, Cambria, "Times New Roman", Times, serif',
  mono: 'Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace',
}

render(<FontFamily fonts={fonts} />)
```

### Font Size

Variant: `typography.fontSize`

Available props:

- fontSizes

fontSizes has to be in the following format:

```
fontSizes: string[] | number[]
```

---

```jsx live noInline
const fontSizes = ["0.875rem", "1rem", "1.25rem", "1.5rem"]

render(<FontSize fontSizes={fontSizes} />)
```

### Font Weight

Variant: `typography.fontWeight`

Available props:

- fontWeights
- previewText (`string`) (optional) - Replace "The quick brown fox jumps over the lazy dog" with something custom

fontWeights has to be in the following format:

```
fontWeights: {
  [key: string]: string
}
```

---

```jsx live noInline
const fontWeights = {
  normal: "400",
  medium: "500",
  semibold: "600",
  bold: "700",
  black: "900",
}

render(<FontWeight fontWeights={fontWeights} />)
```

### Heading

Variants: `typography.heading`, `codeStyles.default`

Available props:

- styles
- theme
- previewText (`string`) (optional) - Replace "Heading" with something custom

If you're already using Theme UI your theme file already has all necessary keys (styles, fontSizes, fontWeights, fonts). As you can see in the below example the `theme.styles` has entries like `h1` which then will be used to display.

You can filter which keys to show by using the helper function `filterStyles({ input, allowed })`.

```jsx live noInline
const theme = {
  fontSizes: ["0.875rem", "1rem", "1.25rem", "1.5rem", "1.875rem", "2.25rem", "3rem", "4rem", "4.5rem"],
  lineHeights: {
    heading: "1.25",
  },
  fontWeights: {
    heading: "700",
  },
  fonts: {
    heading: "inherit",
  },
  styles: {
    root: {
      fontFamily: "body",
      lineHeight: "body",
      fontWeight: "body",
    },
    a: {
      color: "primary",
      textDecoration: "none",
      ":hover": {
        textDecoration: "underline",
      },
    },
    h1: {
      fontFamily: "heading",
      fontWeight: "heading",
      lineHeight: "heading",
      m: 0,
      mb: 1,
      fontSize: 6,
      mt: 2,
    },
    h2: {
      fontFamily: "heading",
      fontWeight: "heading",
      lineHeight: "heading",
      m: 0,
      mb: 1,
      fontSize: 5,
      mt: 2,
    },
    code: {},
    pre: {},
    hr: {
      bg: "muted",
      border: 0,
      height: "1px",
      m: 3,
    },
  },
}

render(
  <Heading
    styles={filterStyles({
      input: theme.styles,
      allowed: [`h1`, `h2`],
    })}
    config={theme}
  />
)
```

### Palette: Color Rows

Available props:

- colors - If you use Theme UI you can use the helper function `normalizeThemeUIColors({ colors })` to convert the array of color strings to the necessary format
- prefix (`string`) (optional) (Default: "")

colors has to be in the following format:

```
colors: {
  name: string
  color: string
}[]
```

---

```jsx live noInline
const theme = {
  colors: {
    indigo: [null, "#ebf4ff", "#c3dafe", "#a3bffa", "#7f9cf5", "#667eea"],
  },
}

const orange = [
  {
    name: "1",
    color: "#feebc8",
  },
  {
    name: "2",
    color: "#fbd38d",
  },
]

render(
  <React.Fragment>
    <Palette colors={normalizeThemeUIColors({ colors: theme.colors.indigo })} prefix="Indigo - " />
    <h4>Orange</h4>
    <Palette colors={orange} />
  </React.Fragment>
)
```

### Palette: Color Swatches

Available props:

- colors - If you use Theme UI you can use the helper function `normalizeThemeUIColors({ colors })` to convert the array of color strings to the necessary format
- prefix (`string`) (optional) (Default: "")
- single (`boolean`) (optional) (Default: false) - When passing in the colors object you can filter out all keys that have arrays as children
- minimal (`boolean`) (optional) (Default: false) - Hide RGB and CMYK label
- mode (`string`) (optional) (Default: "list")

colors has to be in the following format:

```
colors: {
  name: string
  color: string
}[]
```

---

```jsx live noInline
const theme = {
  colors: {
    primary: `#2b6cb0`,
    secondary: `#feb2b2`,
    success: `#9ae6b4`,
    text: `#2d3748`,
    indigo: [null, "#ebf4ff", "#c3dafe", "#a3bffa", "#7f9cf5", "#667eea"],
  },
}

render(<Palette colors={normalizeThemeUIColors({ colors: theme.colors })} minimal mode="swatch" />)
```

### Sizes

Variant: `tables.space`

This theme also exposes a `Table` component. It is used in this case to showcase the different `sizes` of the theme. Generally speaking the `Table` component has the following props:

- columns (`string[]`)
- titles (`string[]`)
- children (`React.ReactNode`)

The `columns` prop is defining the `grid-template-columns` of the table in the fashion of Theme UI ([Responsive Styles](https://theme-ui.com/getting-started#responsive-styles)). The `titles` should be the same count as columns.

So you can define the style for every breakpoint, e.g. ``[`120px 1fr`, `150px 1fr`]`` (120px for everything below the smallest breakpoint, 150px for everything above smallest breakpoint).

You can format the data you want to show how you like but in the end you should have the amount of columns as defined above.

---

```jsx live noInline
const sizes = {
  0: "0",
  1: "0.25rem",
  2: "0.5rem",
  3: "0.75rem",
  4: "1rem",
  5: "1.25rem",
  6: "1.5rem",
  8: "2rem",
  10: "2.5rem",
  12: "3rem",
  16: "4rem",
}

render(
  <Table columns={[`120px 1fr`]} titles={[`Token`, `Value`]} className="sizes-table">
    {Object.entries(sizes).map(([key, value]) => (
      <div key={key}>
        <div>{key}</div>
        <div>{value}</div>
      </div>
    ))}
  </Table>
)
```

### Shadows

Variant: `tables.shadow`

Available props:

- shadows

shadows has to be in the following format:

```
shadows: {
  [key: string]: string
}
```

---

```jsx live noInline
const shadows = {
  default: `0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)`,
  xl: `0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)`,
  "2xl": `0 25px 50px -12px rgba(0, 0, 0, 0.25)`,
}

render(<Shadow shadows={shadows} />)
```

### Space

Available props:

- space

space has to be in the following format:

```
space: string[] | number[]
```

---

```jsx live noInline
const space = [0, `0.25rem`, `0.5rem`, `1rem`, `2rem`, `4rem`, `8rem`]

render(<Space space={space} />)
```

### Video

Variant: `video.specimens`

Available props:

- src (`string`) - Must be a valid path on your webserver, e.g. the video file below is placed inside src/static/videos
- poster (`string`) (optional) (Default: "") - By default the video tag will use the first frame of the video as a preview. With the poster you can define a custom preview image
- name (`string`) (optional) (Default: "")
- muted (`boolean`) (optional) (Default: false)
- loop (`boolean`) (optional) (Default: false)
- autoplay (`boolean`) (optional) (Default: false)

```jsx live
<Video name="LekoArts Themes Model" src="videos/themes_model.mp4" poster="videos/themes_model_poster.png" />
```
